/**
 * Copyright 2020 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.dbfly.mybatis.entity;

import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import com.jianggujin.dbfly.mybatis.table.JDynamicTableName;
import com.jianggujin.dbfly.util.JDBFlyException;

/**
 * 查询条件
 * 
 * @author jianggujin
 *
 */
public class JCondition<T> implements JDynamicTableName {
    protected String orderByClause;

    protected boolean distinct;

    protected boolean exists;

    protected boolean notNull;

    protected boolean forUpdate;

    // 查询字段
    protected Set<String> selectColumns;

    // 排除的查询字段
    protected Set<String> excludeColumns;

    protected String countColumn;

    protected List<JCriteria> oredCriteria;
    protected JEntity entity;

    protected JOrderBy _orderBy;

    /**
     * 动态表名
     */
    private String dynamicTableName;

    /**
     * 默认exists为true
     *
     * @param entityClass
     */
    public JCondition(Class<T> entityClass) {
        this(entityClass, true);
    }

    /**
     * 带exists参数的构造方法，默认notNull为false，允许为空
     *
     * @param entityClass
     * @param exists      - true时，如果字段不存在就抛出异常，false时，如果不存在就不使用该字段的条件
     */
    public JCondition(Class<T> entityClass, boolean exists) {
        this(entityClass, exists, false);
    }

    /**
     * 带exists参数的构造方法
     *
     * @param entityClass
     * @param exists      - true时，如果字段不存在就抛出异常，false时，如果不存在就不使用该字段的条件
     * @param notNull     - true时，如果值为空，就会抛出异常，false时，如果为空就不使用该字段的条件
     */
    public JCondition(Class<T> entityClass, boolean exists, boolean notNull) {
        this.exists = exists;
        this.notNull = notNull;
        this.oredCriteria = new ArrayList<JCriteria>();
        this.entity = JEntityResolver.getEntity(entityClass);
        this._orderBy = new JOrderBy(this, entity);
    }

    public JOrderBy orderBy(String property) {
        this._orderBy.orderBy(property);
        return this._orderBy;
    }

    /**
     * 排除查询字段，优先级低于 selectProperties
     *
     * @param properties 属性名的可变参数
     * @return
     */
    public JCondition<T> excludeProperties(String... properties) {
        if (properties != null && properties.length > 0) {
            if (this.excludeColumns == null) {
                this.excludeColumns = new LinkedHashSet<String>();
            }
            for (String property : properties) {
                if (this.entity.hasProperty(property)) {
                    this.excludeColumns.add(this.entity.getColumn(property).getColumn());
                } else if (exists) {
                    throw new JDBFlyException("类{}中属性{}无效", this.entity.getEntityClass().getCanonicalName(), property);
                }
            }
        }
        return this;
    }

    /**
     * 指定要查询的属性列
     *
     * @param properties
     * @return
     */
    public JCondition<T> selectProperties(String... properties) {
        if (properties != null && properties.length > 0) {
            if (this.selectColumns == null) {
                this.selectColumns = new LinkedHashSet<String>();
            }
            for (String property : properties) {
                if (this.entity.hasProperty(property)) {
                    this.selectColumns.add(this.entity.getColumn(property).getColumn());
                } else if (exists) {
                    throw new JDBFlyException("类{}中属性{}无效", this.entity.getEntityClass().getCanonicalName(), property);
                }
            }
        }
        return this;
    }

    public JCriteria or() {
        JCriteria criteria = createCriteriaInternal();
        criteria.setAndOr("or");
        oredCriteria.add(criteria);
        return criteria.or();
    }

    public JCriteria and() {
        JCriteria criteria = createCriteriaInternal();
        criteria.setAndOr("and");
        oredCriteria.add(criteria);
        return criteria.and();
    }

    private JCriteria createCriteriaInternal() {
        JCriteria criteria = new JCriteria(entity, exists, notNull);
        return criteria;
    }

    /**
     * 清空条件
     */
    public void clear() {
        oredCriteria.clear();
        orderByClause = null;
        distinct = false;
    }

    public String getCountColumn() {
        return countColumn;
    }

    public String getOrderByClause() {
        return orderByClause;
    }

    public void setOrderByClause(String orderByClause) {
        this.orderByClause = orderByClause;
    }

    public List<JCriteria> getOredCriteria() {
        return oredCriteria;
    }

    public Set<String> getSelectColumns() {
        if (excludeColumns != null && excludeColumns.size() > 0) {
            Collection<JEntityColumn> columns = this.entity.getColumns();
            selectColumns = new LinkedHashSet<String>(columns.size() - excludeColumns.size());
            for (JEntityColumn column : columns) {
                if (!excludeColumns.contains(column.getColumn())) {
                    selectColumns.add(column.getColumn());
                }
            }
        }
        return selectColumns;
    }

    public boolean isDistinct() {
        return distinct;
    }

    public void setDistinct(boolean distinct) {
        this.distinct = distinct;
    }

    public boolean isForUpdate() {
        return forUpdate;
    }

    public void setForUpdate(boolean forUpdate) {
        this.forUpdate = forUpdate;
    }

    /**
     * 指定 count(property) 查询属性
     *
     * @param property
     */
    public void setCountProperty(String property) {
        if (this.entity.hasProperty(property)) {
            this.countColumn = this.entity.getColumn(property).getColumn();
        }
    }

    /**
     * 获得实体类
     * 
     * @return
     */
    public Class<?> getEntityClass() {
        return this.entity.getEntityClass();
    }

    @Override
    public String getDynamicTableName() {
        return dynamicTableName;
    }

    public void setDynamicTableName(String dynamicTableName) {
        this.dynamicTableName = dynamicTableName;
    }
}
